/**
 * 
 */
package com.android.zing;

import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map.Entry;

import org.apache.http.HttpException;
import org.apache.http.HttpResponse;
import org.apache.http.HttpVersion;
import org.apache.http.NameValuePair;
import org.apache.http.ParseException;

import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.apache.http.params.HttpProtocolParams;
import org.apache.http.util.EntityUtils;
import org.json.JSONException;
import org.json.JSONObject;

import java.net.HttpURLConnection;
import java.net.URL;

import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;

/**
 * @author trangdoan
 *
 */
public final class ZME_Connection {

	private static final String TAG = "ZME_Connection";

    private static HttpClient client = null;
		
	static {
		KeyStore trustStore = null;
		try {
			trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
		} catch (KeyStoreException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
        try {
			trustStore.load(null, null);
		} catch (NoSuchAlgorithmException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (CertificateException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

        SSLSocketFactory sf = null;
		try {
			sf = new CustomSSLSocketFactory(trustStore);
		} catch (KeyManagementException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (NoSuchAlgorithmException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (KeyStoreException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (UnrecoverableKeyException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
        sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
        
     // Setting up parameters
        HttpParams params = new BasicHttpParams();
        HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
        HttpProtocolParams.setContentCharset(params, "utf-8");
        params.setBooleanParameter("http.protocol.expect-continue", false);

//        // Setting timeout
        HttpConnectionParams.setConnectionTimeout(params, 30000);
        HttpConnectionParams.setSoTimeout(params, 30000);

        // Registering schemes for both HTTP and HTTPS
        SchemeRegistry registry = new SchemeRegistry();
        registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
        registry.register(new Scheme("https", sf, 443));

        // Creating thread safe client connection manager
        ClientConnectionManager ccm = new ThreadSafeClientConnManager(params, registry);

        // Creating HTTP client
        client = new DefaultHttpClient(ccm, params);
	}
	
	
	public static String doRequest(String endpoint, HashMap<String, String> params) throws HttpException, ClientProtocolException, IOException {
		HttpResponse response = null;
    	HttpPost post 		  = null;

    	HttpClient httpclient = getHttpClient();
    	
    	params.put("_ver", ZME_Base.GRAPHAPI_VERSION);
    	params.put("_client_ver", ZME_Base.CLIENT_GRAPHAPI_VERSION);
    	
		post 	 = getPostEntity(endpoint, buildPostData(params));
		response = httpclient.execute(post);
		
		String resp =  EntityUtils.toString( response.getEntity());
		return resp;
	}
			
	private static String read(InputStream in) throws IOException {
	        StringBuilder sb = new StringBuilder();
	        BufferedReader r = new BufferedReader(new InputStreamReader(in), 1000);
	        for (String line = r.readLine(); line != null; line = r.readLine()) {
	            sb.append(line);
	        }
	        in.close();
	        return sb.toString();
	    }
	
	 private static String encodePostBody(HashMap<String, String> parameters, String boundary) {
        if (parameters == null) return "";
        StringBuilder sb = new StringBuilder();

        for (String key : parameters.keySet()) {
            Object parameter = parameters.get(key);
            if (!(parameter instanceof String)) {
                continue;
            }

            sb.append("Content-Disposition: form-data; name=\"" + key +
                    "\"\r\n\r\n" + (String)parameter);
            sb.append("\r\n" + "--" + boundary + "\r\n");
        }

        return sb.toString();
    }
	
	
	public static Object request( String endpoint, HashMap<String, String> params) 
			throws HttpException, ClientProtocolException, IOException, ZME_Error{
		Log.d(TAG, ">>>> ZME_Connection :: do request with endpoint '" + endpoint + "'");
		long startTime = System.currentTimeMillis();
		Object ret = null;
		JSONObject jsonResp = null;		
				
		String resp =  doRequest(endpoint, params);
		
		try {
			Log.d(TAG, ">>>> ZME_Connection:: response => " + resp);
			jsonResp 	 = parseResponseToJSON(resp);			
			
			if(jsonResp != null) {
				Long errorCode = jsonResp.getLong("error_code");				
				
				if(errorCode != 0) {
					String errorType = "";
					String errorMessage = "";
					
					if(jsonResp.has("error_type")) {
						errorType = jsonResp.getString("error_type");
					}
					
					if(jsonResp.has("error_message")) {
						errorMessage = jsonResp.getString("error_message");
					}
					else {
						errorMessage = "unknown error";
					}
					throw new ZME_Error(errorMessage, errorType, errorCode.intValue());
				}				
				ret = jsonResp.get("data");
				
			}
			else {
				throw new ZME_Error("response null", -9999);
			}
			
		}
		catch(ZME_Error e) {
			throw e;
		}
		catch (Exception e) {
			e.printStackTrace();
			ret = null;
		}
		long diff = System.currentTimeMillis() - startTime;
		Log.d(TAG, "<<<< ZME_Connection :: do request with endpoint '" + endpoint + "'-elapse=" + diff + " msecs");
		return ret;
	}
	
	private static List<NameValuePair> buildPostData( HashMap<String, String> params ) {
		List<NameValuePair> postData = new ArrayList<NameValuePair>();
		
		for( Entry<String, String> entry: params.entrySet() ){
			postData.add(new BasicNameValuePair( entry.getKey(), entry.getValue()));
		}
		
		return postData;
	}
	
	private static HttpPost getPostEntity( String endpoint, List<NameValuePair> params ) 
		throws HttpException {
    	
        HttpPost post = new HttpPost( endpoint );

        try {
			post.setEntity(new UrlEncodedFormEntity( params ));
		} catch (UnsupportedEncodingException e) {
			Log.d(TAG, "Got exception in getPostEntity(). Cannot initialize parameter for post request!");
			e.printStackTrace();
			throw new HttpException("Unable to initialize POST request");
		}
    	
    	return post;
    }
	
	private static JSONObject parseResponseToJSON( String resp ) 
			throws ParseException, JSONException, IOException {
		JSONObject data = null;
		
		data = new JSONObject(resp); 
		
		return data;
	}	
	
	public static HttpClient getHttpClient() {
		
		return client;
	}	
}
